/*
 Xern Entity Relocation Network
 Copyright (C) 2010-2011 Ronie Salgado <roniesalg@gmail.com>

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/

#include <stdio.h>
#include <string.h>
#include <boost/uuid/uuid_generators.hpp>

#include "Xern/IGeometryInstance.hpp"
#include "Xern/IPhysicsEngine.hpp"
#include "Xern/IRenderer.hpp"

#include "Xern/Log.hpp"
#include "Xern/Exceptions.hpp"
#include "Xern/FileSystem.hpp"
#include "Xern/RegionLoader.hpp"
#include "Xern/RegionFile.hpp"
#include "Xern/RegionMesh.hpp"
#include "Xern/ResourceManager.hpp"
#include "Xern/StringTable.hpp"
#include "Xern/Engine.hpp"
#include "Xern/WorldManager.hpp"

namespace Xern
{
	boost::shared_ptr<Region> RegionLoader::Load(const std::string &path, boost::shared_ptr<Region> base)
	{
		if(path.empty())
			throw XernException("Failed to load empty region");

		std::string ext = FileExtension(path);
		if(ext == ".scene")
			return LoadXmlRegion(path, base);
		else if(ext == ".xrgn")
			return LoadBinaryRegion(path, base);
		else
			throw XernException("Unsupported region extension '" + ext + "'");
	}
	boost::shared_ptr<Region> RegionLoader::LoadXmlRegion(const std::string &path, boost::shared_ptr<Region> base)
	{
		if(path.empty())
			throw XernException("Failed to load empty region");

		// Load the document.
		TiXmlDocument doc;
		if(!doc.LoadFile(path.c_str()))
			throw XernException("Failed to load region file '" + path +
					"' :" + doc.ErrorDesc());

		// Get the top element.
		TiXmlElement *root = doc.RootElement();
		if(!root)
			throw XernException("Region file '" + path + "' is empty");

		// Create the region.
		boost::shared_ptr<Region> ret = base;
		if(!ret)
			ret.reset(new Region());

		// Read the region id.
		const char *guid = root->Attribute("guid");
		if(guid)
			ret->SetId(uuid_parser(guid));

		// Get the nodes element.
		TiXmlElement *nodes = root->FirstChildElement("nodes");

		// Read the nodes.
		if(nodes)
		{
			TiXmlElement *node = nodes->FirstChildElement("node");
			for(; node; node = node->NextSiblingElement("node"))
				LoadNode(node, ret, base);
		}

		return ret;
	}

	boost::shared_ptr<Region> RegionLoader::LoadFromResource(const std::string &name, const std::string &group, boost::shared_ptr<Region> base)
	{
		// Find the region resource.
		boost::shared_ptr<ResourceManager> resMan = Engine::Get().GetResourceManager();
		std::string path = resMan->FindResourcePath(name, group);
		if(path.empty())
		{
			// If there isn't the extension, add it.
			if(name.rfind('.') == std::string::npos)
			{
				path = resMan->FindResourcePath(name + ".scene", group);
				if(path.empty())
					path = resMan->FindResourcePath(name + ".xrgn", group);
			}

			if(path.empty())
				throw XernException("Failed to get resource [" + group + "]" + name );
		}

		return Load(path, base);
	}

	void RegionLoader::LoadNode(TiXmlElement *node, boost::shared_ptr<Entity> parent, boost::shared_ptr<Region> base)
	{
		// Get the world manager
		boost::shared_ptr<WorldManager> worldMan = Engine::Get().GetWorldManager();

		// Get the renderer.
		boost::shared_ptr<IRenderer> renderer = Engine::Get().GetRenderer();

		// Get the physics engine.
		boost::shared_ptr<IPhysicsEngine> physicsEngine = Engine::Get().GetPhysicsEngine();

		// Create the entity.
		boost::shared_ptr<Entity> ent;

		// Read the guid.
		const char *guid = node->Attribute("guid");
		uuid entGuid;
		if(guid)
		 entGuid = uuid_parser(guid);

		// Try first with the class id.
		const char *classId = node->Attribute("classGuid");
		if(classId)
		{
			uuid classGuid = uuid_parser(classId);
			if(base)
				ent = base->CreateEntity(classGuid, entGuid);
			else
				ent = worldMan->CreateEntity(classGuid, entGuid);
			if(!ent)
				LogConsole->Warning("Unregistered class id {%s}", classId);
		}

		// Read the classname
		const char *className = node->Attribute("className");
		if(!ent && className)
		{
			if(base)
				ent = base->CreateEntity(className, entGuid);
			else
				ent = worldMan->CreateEntity(className, entGuid);

			if(!ent)
				LogConsole->Error("Failed to create an entity[ClassName = %s]", className);;
		}

		// Create a generic entity.
		if(!ent)
		{
			if(base)
				ent = base->CreateEntity();
			else
				ent.reset(new Entity());
		}

		// Add to the parent.
		if(parent)
			parent->AddChild(ent);

		// Read the name.
		const char *name = node->Attribute("name");
		if(name)
			ent->SetName(name);

		// Read the transformation.
		OrthoTransform trans;

		// Read the position
		TiXmlElement *position = node->FirstChildElement("position");
		if(position)
			trans.SetVector(ReadVector(position));

		// Read the rotation.
		TiXmlElement *rotation = node->FirstChildElement("rotation");
		if(rotation)
			trans.SetMatrix(QRotMatrix3(ReadQuaternion(rotation)));

		// Read the scale
		TiXmlElement *scale = node->FirstChildElement("scale");
		if(scale)
		{
			Vector3 vs = ReadVector(scale);
			if(CompareVector3(vs, Vector3(1.0f)) != 0)
				LogConsole->Warning("Scale is not supported in scene graph!.");
		}
		ent->SetTransform(trans);

		// Read the properties.
		TiXmlElement *properties = node->FirstChildElement("properties");
		if(properties)
		{
			TiXmlElement *prop = properties->FirstChildElement("prop");
			for(; prop; prop = prop->NextSiblingElement("prop"))
			{
				const char *key = prop->Attribute("key");
				const char *value = prop->Attribute("value");
				if(!key || !value)
					continue;

				// Parse the properties.
				ent->ParseProperty(key, value);
			}
		}

		// Load the geometry(optional).
		TiXmlElement *geom = node->FirstChildElement("entity");
		for(; geom; geom = geom->NextSiblingElement("entity"))
		{
			const char *name = geom->Attribute("name");
			const char *meshFile = geom->Attribute("meshFile");

			// Check for errors;
			if(!meshFile)
			{
				if(name)
					LogConsole->Error("Geometry '%s' without mesh.", name);
				continue;
			}

			// Todo: Parse and process the static property.

			// Create the geometry.
			boost::shared_ptr<IGeometry> geometry;
			if(name)
				geometry = renderer->CreateGeometry(name, meshFile, "Maps");
			else
				geometry = renderer->CreateGeometry(meshFile, "Maps");

			// Check for errors.
			if(!geometry)
			{
				LogConsole->Error("Failed to load geometry '%s'", meshFile);
				continue;
			}

			// Add the geometry into the entity.
			ent->AddResource(geometry->CreateInstance());
		}

		// Load the lights(optional)
		TiXmlElement *lnode = node->FirstChildElement("light");
		for(; lnode; lnode = lnode->NextSiblingElement("light"))
		{
			// Create the light.
			boost::shared_ptr<ILight> light;

			const char *name = lnode->Attribute("name");
			if(name)
				light = renderer->CreateLight(name);
			else
				light = renderer->CreateLight();

			// Read the type.
			const char *type = lnode->Attribute("type");
			if(type)
			{
				if(!strcmp(type, "point"))
					light->SetType(ILight::LT_POINT);
				else if(!strcmp(type, "directional"))
					light->SetType(ILight::LT_DIRECTIONAL);
				else if(!strcmp(type, "spot"))
					light->SetType(ILight::LT_SPOT);
			}

			// Read the diffuse color.
			TiXmlElement *diffuse = lnode->FirstChildElement("colourDiffuse");
			if(diffuse)
				light->SetDiffuseColor(ReadColor(diffuse));

			// Read the specular
			TiXmlElement *specular = lnode->FirstChildElement("colourSpecular");
			if(specular)
				light->SetSpecularColor(ReadColor(specular));

			// Read the attenuation.
			TiXmlElement *attenuation = lnode->FirstChildElement("lightAttenuation");
			if(attenuation)
			{
				float range = 100000.0f;
				float constant = 1.0;
				float linear = 0.0f;
				float quadratic = 0.0f;

				// Read the range.
				const char *vstr = attenuation->Attribute("range");
				if(vstr)
					range = atof(vstr);

				// Read the constant.
				vstr = attenuation->Attribute("constant");
				if(vstr)
					constant = atof(vstr);

				// Read the linear factor.
				vstr = attenuation->Attribute("linear");
				if(vstr)
					linear = atof(vstr);

				// Read the quadratic factor.
				vstr = attenuation->Attribute("quadratic");
				if(vstr)
					quadratic = atof(vstr);

				// Set the light attenuation.
				light->SetAttenuation(range, constant, linear, quadratic);
			}

			// Add the light into the entity.
			ent->AddResource(light);
		}

		// Load the physic(optional)
		TiXmlElement *physics = node->FirstChildElement("physics");
		if(physics)
		{
			// Read the type.
			const char *type = physics->Attribute("type");
			if(!type)
				type = "static";

			// Read the mass.
			const char *smass = physics->Attribute("mass");
			float mass = 1.0f;
			if(smass)
				mass = atof(smass);

			// Read the collision shapes.
			boost::shared_ptr<ICollisionShape> shape, newshape;
			TiXmlElement *shapeNode = physics->FirstChildElement();
			for(; shapeNode; shapeNode = shapeNode->NextSiblingElement())
			{
				const char *shapeType = shapeNode->Value();
				assert(shapeType);

				if(!strcmp(shapeType, "box"))
				{
					// Minimal point.
					Vector3 min(-1.0f);
					TiXmlElement *minNode = shapeNode->FirstChildElement("min");
					if(minNode)
						min = ReadVector(minNode);

					// Maximum point.
					Vector3 max(1.0f);
					TiXmlElement *maxNode = shapeNode->FirstChildElement("max");
					if(maxNode)
						max = ReadVector(maxNode);

					// Calculate the half extent.
					Vector3 halfExtent = (max-min)*0.5f;

					// Create the box shape.
					newshape = physicsEngine->CreateBoxShape(halfExtent);
				}
				else if(!strcmp(shapeType, "sphere"))
				{
					// Read the sphere radius.
					float radius = 1.0f;
					const char *sradius = shapeNode->Attribute("radius");
					if(sradius)
						radius = atof(sradius);

					newshape = physicsEngine->CreateSphereShape(radius);
				}
				else if(!strcmp(shapeType, "convexHull"))
				{
					// Read the number of points.
					int numpoints = 0;
					const char *snumpoints = shapeNode->Attribute("numpoints");
					if(!snumpoints)
					{
						LogConsole->Error("Empty convex hull shape.");
						continue;
					}
					numpoints = atoi(snumpoints);

					// Create an array for each one of the points.
					Vector3 *points = new Vector3[numpoints];
					int i = 0;
					TiXmlElement *point = shapeNode->FirstChildElement("point");
					for(; point; point = point->NextSiblingElement("point"))
					{
						if(i >= numpoints)
						{
							LogConsole->Warning("Convex has more points than the declared. Ignoring them.");
							break;
						}
						points[i++] = ReadVector(point);
					}

					// Check the point array.
					if(i == 0)
					{
						LogConsole->Error("Empty convex hull[numpoints = %d].", numpoints);
						continue;
					}
					else if(i < numpoints)
					{
						LogConsole->Error("Incomplete convex hull[numpoints = %d, actualpoints = %d]", numpoints, i);
						continue;
					}

					// Create the shape.
					newshape = physicsEngine->CreateConvexHull(numpoints, points);

					// Delete the points array.
					delete [] points;
				}
				else
				{
					LogConsole->Error("Unsupported collision shape '%s'", shapeType);
					continue;
				}

				if(!shape)
				{
					shape = newshape;
				}
				else
				{
					// TODO: add compound shape support.
					assert(NULL);
				}
			}

			// Ignore if there isn't a shape.
			if(shape)
			{
				// Parse the body type and create it.
				if(!strcmp(type, "rigid_body"))
				{
					boost::shared_ptr<IRigidBody> body = physicsEngine->CreateRigidBody(mass, shape, ent);
					ent->SetRigidBody(body);
				}
				else if(!strcmp(type, "static"))
				{
					boost::shared_ptr<IRigidBody> body = physicsEngine->CreateStaticBody(shape, ent);
					ent->SetRigidBody(body);
				}
				else
				{
					LogConsole->Error("Unknown body type '%s'", type);
				}
			}
		}

		// Load the sub nodes.
		TiXmlElement *subnode = node->FirstChildElement("node");
		for(; subnode; subnode = subnode->NextSiblingElement("node"))
			LoadNode(subnode, ent, base);
	}

	Vector3 RegionLoader::ReadVector(TiXmlElement *node)
	{
		Vector3 ret;
		const char *v = node->Attribute("x");
		if(v)
			ret.x = atof(v);
		v = node->Attribute("y");
		if(v)
			ret.y = atof(v);
		v = node->Attribute("z");
		if(v)
			ret.z = atof(v);
		return ret;
	}

	Quaternion RegionLoader::ReadQuaternion(TiXmlElement *node)
	{
		Quaternion ret;
		const char *v = node->Attribute("w");
		if(v)
			ret.w = atof(v);
		v = node->Attribute("x");
		if(v)
			ret.x = atof(v);
		v = node->Attribute("y");
		if(v)
			ret.y = atof(v);
		v = node->Attribute("z");
		if(v)
			ret.z = atof(v);
		return ret;
	}

	Color RegionLoader::ReadColor(TiXmlElement *node)
	{
		Color ret;
		const char *v = node->Attribute("r");
		if(v)
			ret.r = atof(v);
		v = node->Attribute("g");
		if(v)
			ret.g = atof(v);
		v = node->Attribute("b");
		if(v)
			ret.b = atof(v);
		v = node->Attribute("a");
		if(v)
			ret.a = atof(v);
		return ret;
	}

	// File managing utility.
	class FileHandle
	{
	public:
		FileHandle(FILE *file)
		 : file(file) {}
		~FileHandle()
		{
			fclose(file);
		}

	private:
		FILE *file;
	};

	// Binary region loader
	boost::shared_ptr<Region>
	RegionLoader::LoadBinaryRegion(const std::string &path, boost::shared_ptr<Region> base)
	{
		if(path.empty())
			throw XernException("Failed to load empty region");

		// Open the region.
		FILE *file = fopen(path.c_str(), "rb");
		FileHandle fh(file);
		if(!file)
			throw XernException("Failed to to open the region");

		// Create the data stream.
		DataStream in(file);

		// Read the region header.
		RegionFileHeader header;
		in >> header;

		// Check the signature.
		if(header.signature != RegionSignature)
			throw XernException("Invalid region file");

		// Check the header size.
		if(header.headerSize != RegionHeaderSize)
			throw XernException("Unsupported region header version");

		// Read the string table.
		stringTable.reset(new StringTable());
		fseek(file, header.stringTableOffset, SEEK_SET);
		stringTable->ReadStringTable(in);

		// Read the mesh table.
		fseek(file, header.meshTableOffset, SEEK_SET);
		size_t end = header.meshTableOffset + header.meshTableSize;
		while(ftell(file) < end)
			ReadRegionMesh(in);

		// Read the collision shapes.
		fseek(file, header.collisionTableOffset, SEEK_SET);
		end = header.collisionTableOffset + header.collisionTableSize;
		while(ftell(file) < end)
			ReadCollisionShape(in);

		// Read again the collision shape, to fill the hierarchy.
		fseek(file, header.collisionTableOffset, SEEK_SET);
		size_t numshapes = shapes.size();
		for(size_t i = 0; i < numshapes; i++)
			ReadCollisionShapeCompound(in, shapes[i]);

		// Create the objects.
		objects.reserve(header.numobjects);
		fseek(file, header.objectTableOffset, SEEK_SET);

		// First pass: create the objects.
		for(unsigned i = 0; i < header.numobjects; i++)
			ReadRegionObject(in, base);

		// Second pass: build hierarchy.
		fseek(file, header.objectTableOffset, SEEK_SET);
		for(unsigned i = 0; i < header.numobjects; i++)
			ReadObjectHierarchy(in, objects[i]);

		if(header.rootObject >= objects.size())
			throw XernException("Invalid root object");

		boost::shared_ptr<Entity> root = objects[header.rootObject];
		boost::shared_ptr<Region> ret = boost::dynamic_pointer_cast<Region> (root);
		if(!ret)
			throw XernException("Invalid root object");
		ret->SetFileName(BaseName(path));
		return ret;
	}

	void RegionLoader::ReadRegionMesh(DataStream &in)
	{
		boost::shared_ptr<RegionMesh> mesh(new RegionMesh);
		RegionFileMesh fmesh;
		in >> fmesh;

		// Read mesh data.
		mesh->name = stringTable->GetString(fmesh.name);
		mesh->vertexSize = fmesh.vertexSize;
		mesh->vertexFlags = fmesh.vertexFlags;
		mesh->numVertices = fmesh.numvertices;

		// Read the bounding box.
		AABox &box = mesh->boundingBox;
		box.min.x = fmesh.boundingBoxMin[0];
		box.min.y = fmesh.boundingBoxMin[1];
		box.min.z = fmesh.boundingBoxMin[2];
		box.max.x = fmesh.boundingBoxMax[0];
		box.max.y = fmesh.boundingBoxMax[1];
		box.max.z = fmesh.boundingBoxMax[2];

		// Create vertex buffer.
		size_t vertexLen = mesh->numVertices*mesh->vertexSize;
		mesh->vertexBuffer.reset(new unsigned char[vertexLen]);

		// Read submesh.
		mesh->subMeshes.reserve(fmesh.numsubmeshes);
		for(unsigned i = 0; i < fmesh.numsubmeshes; i++)
		{
			// Create submesh
			boost::shared_ptr<RegionSubMesh> submesh(new RegionSubMesh);

			// Read submesh
			RegionFileSubMesh fsubmesh;
			in >> fsubmesh;

			submesh->materialName = stringTable->GetString(fsubmesh.texture);
			submesh->indexSize = fsubmesh.indexSize;
			submesh->numTriangles = fsubmesh.numtriangles;

			// Create index buffer
			size_t indexLen = submesh->indexSize*submesh->numTriangles*3;
			submesh->triangles.reset(new unsigned char[indexLen]);

			// Read triangles
			in.Read(indexLen, submesh->triangles.get());

			// Store submesh.
			mesh->subMeshes.push_back(submesh);
		}

		// Read vertex buffer.
		in.Read(vertexLen, mesh->vertexBuffer.get());

		// Create the geometry.
		boost::shared_ptr<IRenderer> renderer = Engine::Get().GetRenderer();
		if(renderer)
		{
			mesh->geometry = renderer->CreateGeometry(mesh);

			// Register the geometry.
			uuid id;
			for(int i = 0; i < 16; i++)
				id.data[i] = fmesh.uuid[i];
			if(!id.is_nil())
			{
				mesh->geometry->SetId(id);
				boost::shared_ptr<ResourceManager> resourceManager = Engine::Get().GetResourceManager();
				resourceManager->RegisterExternalResource(mesh->geometry);
			}
		}
		meshes.push_back(mesh);
	}

	void RegionLoader::ReadCollisionShape(DataStream &in)
	{
		boost::shared_ptr<ICollisionShape> shape;

		// Get the physics engine.
		boost::shared_ptr<IPhysicsEngine> physics =
				Engine::Get().GetPhysicsEngine();

		// Read the collision shape header.
		RegionFileCollisionShape fshape;
		in >> fshape;

		// Continue reading according to the type.
		switch(fshape.type)
		{
		case RFST_SPHERE:
			{
				// Create the shape
				shape = physics->CreateSphereShape(fshape.sphere.radius);
				shapes.push_back(shape);
			}
			break;
		case RFST_BOX:
			{
				// Read the box.
				Vector3 min, max;
				min.x = fshape.box.min[0];
				min.y = fshape.box.min[1];
				min.z = fshape.box.min[2];

				max.x = fshape.box.max[0];
				max.y = fshape.box.max[1];
				max.z = fshape.box.max[2];

				// Create the shape.
				shape = physics->CreateBoxShape((max - min)*0.5f);
				shapes.push_back(shape);
			}
			break;
		case RFST_CONVEX_HULL:
			{
				// Create a buffer for the planes.
				int numplanes = fshape.convexHull.numplanes;
				Plane *planes = new Plane[numplanes];

				// Read the planes.
				for(int i = 0; i < numplanes; i++)
				{
					Plane &p = planes[i];
					in >> p.a >> p.b >> p.c >> p.d;
				}

				// Create the shape.
				shape = physics->CreateConvexHull(numplanes, planes);
				shapes.push_back(shape);

				// Delete the buffer.
				delete [] planes;
			}
			break;
		case RFST_COMPOUND:
			{
				// Skip the compound data.
				in.Skip(RegionFileSubCollisionShape::Size*fshape.compound.numsubshapes);

				// Just create the shape.
				shape = physics->CreateCompoundShape();
				shapes.push_back(shape);
			}
			break;
		}

		// Register the collision shape
		uuid id;
		for(int i = 0; i < 16; i++)
			id.data[i] = fshape.uuid[i];
		if(!id.is_nil())
		{
			// Rename the shape.
			shape->SetId(id);
			Engine::Get().GetResourceManager()->RenameResource(shape, id);
		}
	}

	void RegionLoader::ReadCollisionShapeCompound(DataStream &in,
			boost::shared_ptr<ICollisionShape> shape)
	{
		// Read the collision shape header.
		RegionFileCollisionShape fshape;
		in >> fshape;

		// Skip shapes.
		if(fshape.type == RFST_CONVEX_HULL)
		{
			in.Skip(sizeof(f32)*4*fshape.convexHull.numplanes);
			return;
		}

		// Ignore shapes that aren't compound.
		if(fshape.type != RFST_COMPOUND)
			return;

		// This must be a compound shape
		boost::shared_ptr<ICompoundShape> compound =
				boost::dynamic_pointer_cast<ICompoundShape> (shape);
		assert(compound);

		// Read the childs.
		for(unsigned i = 0; i < fshape.compound.numsubshapes; i++)
		{
			RegionFileSubCollisionShape fsub;
			in >> fsub;

			// Read the child.
			if(fsub.shape >= shapes.size())
				continue;

			boost::shared_ptr<ICollisionShape> child = shapes[fsub.shape];

			// Read the position.
			Vector3 pos;
			pos.x = fsub.position[0];
			pos.y = fsub.position[1];
			pos.z = fsub.position[2];

			// Read the rotation.
			Quaternion rot;
			rot.w = fsub.rotation[0];
			rot.x = fsub.rotation[1];
			rot.y = fsub.rotation[2];
			rot.z = fsub.rotation[3];

			// Create the transform.
			OrthoTransform trans;
			trans.SetVector(pos);
			trans.SetMatrix(QRotMatrix3(rot));

			// Add the subshape.
			compound->AddShape(trans, child);
		}
	}

	void RegionLoader::ReadRegionObject(DataStream &in, boost::shared_ptr<Region> base)
	{
		RegionFileObject fobject;
		in >> fobject;

		std::string className = stringTable->GetString(fobject.className);
		boost::shared_ptr<WorldManager> worldMan =
				Engine::Get().GetWorldManager();

		// Create the object.
		boost::shared_ptr<Entity> object;
		if(className == "worldspawn" || className == "region")
		{
			if(base)
				object = base;
			else
				object.reset(new Region());
		}
		else
		{
			// Allow remote object creation.
			if(base)
				object = base->CreateEntity(className);
			else
				object = worldMan->CreateEntity(className);
		}

		// Create a dummy entity.
		if(!object)
		{
			if(base)
				object = base->CreateEntity();
			else
				object.reset(new Entity());
		}

		// Read the object data.
		object->SetName(stringTable->GetString(fobject.name));

		// Read the id.
		uuid id;
		for(int i = 0; i < 16; i++)
			id.data[i] = fobject.uuid[i];
		if(!id.is_nil())
			object->SetId(id);

		// Read the position.
		Vector3 position;
		position.x = fobject.position[0];
		position.y = fobject.position[1];
		position.z = fobject.position[2];

		// Read the matrix.
		Matrix3 mat;
		mat.m1m1 = fobject.matrix[0];
		mat.m1m2 = fobject.matrix[1];
		mat.m1m3 = fobject.matrix[2];
		mat.m2m1 = fobject.matrix[3];
		mat.m2m2 = fobject.matrix[4];
		mat.m2m3 = fobject.matrix[5];
		mat.m3m1 = fobject.matrix[6];
		mat.m3m2 = fobject.matrix[7];
		mat.m3m3 = fobject.matrix[8];

		// Set the transform.
		OrthoTransform trans;
		trans.SetVector(position);
		trans.SetMatrix(mat);
		object->SetTransform(trans);

		// Read the meshes.
		for(unsigned i = 0; i < fobject.nummeshes; i++)
		{
			u16 meshId;
			in >> meshId;
			boost::shared_ptr<RegionMesh> mesh = meshes[meshId];
			if(mesh && mesh->geometry)
			{
				// Create the geometry instance.
				object->AddResource(mesh->geometry->CreateInstance());
			}
		}

		// Skip the children.
		in.Skip(fobject.numchildren * sizeof(u16));

		// Read the properties.
		for(unsigned i = 0; i < fobject.numproperties; i++)
		{
			u16 key, value;
			in >> key >> value;
			object->ParseProperty(stringTable->GetString(key),
					stringTable->GetString(value));
		}

		// Read the collision shapes.
		boost::shared_ptr<ICollisionShape> shape;
		for(unsigned i = 0; i < fobject.numcollisionShapes; i++)
		{
			// Read the shape id.
			u16 shapeId;
			in >> shapeId;

			// Ignore invalid shapes.
			if(shapeId >= shapes.size())
				continue;

			shape = shapes[shapeId];
			break; // Ignore other shapes.
		}

		// Create the rigid body.
		if(shape)
		{
			// Get the physics engine.
			boost::shared_ptr<IPhysicsEngine> physics = Engine::Get().GetPhysicsEngine();

			// Create the body.
			boost::shared_ptr<IRigidBody> body;

			switch(fobject.bodyType)
			{
			case RFBT_RIGID:
				body = physics->CreateRigidBody(fobject.mass, shape, object);
				break;
			case RFBT_STATIC:
			default:
				body = physics->CreateStaticBody(shape, object);
				break;
			}

			// Set the object body.
			object->SetRigidBody(body);
		}

		// Store the object.
		objects.push_back(object);
	}

	void RegionLoader::ReadObjectHierarchy(DataStream &in,
			boost::shared_ptr<Entity> object)
	{
		// Read the header
		RegionFileObject fobject;
		in >> fobject;

		// Ignore the meshes.
		in.Skip(fobject.nummeshes * sizeof(u16));

		// Read the children.
		for(unsigned i = 0; i < fobject.numchildren; i++)
		{
			u16 child;
			in >> child;

			if(child >= objects.size())
				continue;

			object->AddChild(objects[child]);
		}

		// Ignore the properties.
		in.Skip(fobject.numproperties * sizeof(u16) * 2);

		// Ignore the collision shapes.
		in.Skip(fobject.numcollisionShapes * sizeof(u16));
	}

}; // namespace Xern
